package sketch.specs;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.eclipse.jdt.core.dom.Block;

import sketch.ast.ASTUtils;
import sketch.util.Checker;

public class IdentifierBlockExpander {
	
	public final Block realBlock;
	
	public final List<SketchedBlock> childrenBlocks;
	
	public static List<Block> expandOnIdentifier(Block real_block, List<SketchedBlock> childrenBlocks, List<Block> blocksToExpand) {
		IdentifierBlockExpander expander = new IdentifierBlockExpander(real_block, childrenBlocks);
		return expander.expandOnIdentifierBlocks(blocksToExpand);
	}
	
	private IdentifierBlockExpander(Block real_block, List<SketchedBlock> childrenBlocks) {
		Checker.checkNull(real_block, "The block input could not be null.");
		Checker.checkNull(childrenBlocks, "The childrenBlocks input could not be null.");
		this.realBlock = real_block;
		this.childrenBlocks = childrenBlocks;
	}

	private List<Block> expandOnIdentifierBlocks(List<Block> blocks) {
		Map<String, List<Block>> idBlocksAfterClassification = this.getIdentifierBlocks();
		if(idBlocksAfterClassification.isEmpty()) {
			//do nothing here
			return blocks;
		}
		
		//get all block
		List<Block> idBlocks = new LinkedList<Block>();
		for(List<Block> blist : idBlocksAfterClassification.values()) {
			idBlocks.addAll(blist);
		}
		
		List<Block> all = new LinkedList<Block>();
		for(Block b : blocks) {
			all.addAll(this.expandOnSingleIdentifierBlock(b, idBlocksAfterClassification, idBlocks));
		}
		
		Checker.checkTrue(blocks.size() * idBlocksAfterClassification.size() == all.size(),
				"Does not replicate correctly. all size: " + all.size() + ",  idBlocks size: "
				+ idBlocksAfterClassification.size() + ",  existing blocks size: " + blocks.size());
		
		return all;
	}
	
	private List<Block> expandOnSingleIdentifierBlock(Block block, Map<String, List<Block>> idBlocks, Collection<Block> allBlocks) {
		Checker.checkTrue(!idBlocks.isEmpty(), "The given id blocks should not be empty!");
		Checker.checkTrue(block.getStartPosition() == this.realBlock.getStartPosition(), "Two blocks are not the same!");
		
		List<Block> singleIdBlocks = new LinkedList<Block>();
		
		for(List<Block> blist : idBlocks.values()) {
			Collection<Block> needToRemove = new LinkedList<Block>();
			for(Block b : allBlocks) {
				if(!blist.contains(b)) {
					needToRemove.add(b);
				}
			}
			//remove that
			BlockRemoveVisitor visitor = new BlockRemoveVisitor(needToRemove);
			Block replicate_block = ASTUtils.deepClone(block);
			replicate_block.accept(visitor);
			singleIdBlocks.add(replicate_block);
		}
		
		return singleIdBlocks;
	}
	
	//classifies all children blocks based on its id
	private Map<String, List<Block>> getIdentifierBlocks() {
		Map<String, List<Block>> idBlocks = new LinkedHashMap<String, List<Block>>();
		
		for(SketchedBlock sketechedBlock : this.childrenBlocks) {
			if(sketechedBlock instanceof SketchedIdentifierBlock) {
				SketchedIdentifierBlock idBlock = (SketchedIdentifierBlock)sketechedBlock;
				String id = idBlock.identifier;
				Block real_block = idBlock.realBlock;
				if(!idBlocks.containsKey(id)) {
					idBlocks.put(id, new LinkedList<Block>());
				}
				idBlocks.get(id).add(real_block);
			}
		}
		
		return idBlocks;
	}
}